package com.hl.learndagger;


import com.hl.learndagger.di.componet.DaggerDemoComponent;
import com.hl.learndagger.di.componet.DaggerDemoMyScopeComponent;
import com.hl.learndagger.di.componet.DemoComponent;
import com.hl.learndagger.di.componet.DemoMyScopeComponent;
import com.hl.learndagger.di.module.DemoModule;

import javax.inject.Inject;

public class TestDagger {

    @Inject
    public User user1;

    @Inject
    public User user2;

    @Inject
    public VIPUser vipUser;

    public TestDagger()
    {
        DaggerDemoComponent.builder()
                .demoModule(new DemoModule())
                .build()
                .inject(this);
    }

    public TestDagger(DemoModule demoModule)
    {
        DaggerDemoComponent.builder()
                .demoModule(demoModule)
                .build()
                .inject(this);
    }

    public TestDagger(DemoComponent demoComponent)
    {
        demoComponent.inject(this);
    }


    public static void main(String[] args)
    {
        //printInfoUserFromConstructor();

        //printInfoVipUserFromModule();

        //compareUser1WithUser2();

        //compareUser1WithUser2WithSameModule();

        //compareUser1WithUser2WithSameComponent();

        //compareSuperUser1WithSuperUser2();

        printVipUserWithQualifier();
    }

    /**
     * @Inject有两个作用，一个约定：
     * 作用：
     *  1、标记类属性，表示这个属性将被注入
     *  2、标记类的构造函数，表示这个类会是注入的一个来源
     * 约定：
     *  1、被标记的属性的访问修饰符要是public
     * */
    private static void printInfoUserFromConstructor()
    {
        TestDagger testDagger = new TestDagger();
        System.out.println("name: "+testDagger.user1.getName()+"  password: "+testDagger.user1.getPassword());
    }

    /**
     * @Module的作用：
     *   多数情况下，我们是无法将@Inject标记打到一个类的构造函数上，
     * 比如使用的类来至第三库、Android系统自带类等。
     * 虽然无法直接打上@Inject标签，但是可以提供一个类的实例，
     * 注入时用这些实例初始化被标记的属性。管理这些实例的类叫做Module，
     * 在Dagger中用@Module来标记这些类。
     *
     * @Provides的作用：
     * 标记Module类中提供实例的方法
     *
     * @Component的作用：
     *   有了注入的目标（被@Inject标记的属性），有了被注入类的实例来源（构造函数、Module），
     * 剩下的事情就是注入
     * */
    private static void printInfoVipUserFromModule()
    {
        TestDagger testDagger = new TestDagger();
        System.out.println("name: "+testDagger.vipUser.getName() +
                "  password: "+testDagger.vipUser.getPassword()+
                " Vip number: "+testDagger.vipUser.getVipNumber());
    }

    /**
     * @Scope的作用：
     *  1、标记Provides标记的方法
     *  2、标记有Inject标记的构造函数所在的类
     *  Scope的具体含义不是太清楚，但是从下面几个测试方法来看，
     *  同一个Component来完成注入，那么被@Singleton标记的实例类或者Provides方法，都会提供相同的对象
     *  换句话说使用Singleton注解创建出来的单例的生命周期和Component一样
     * @Singleton的使用
     * */
    private static void compareUser1WithUser2()
    {
        TestDagger testDagger1 = new TestDagger();
        TestDagger testDagger2 = new TestDagger();
        System.out.println("testDagger1:\n user1: "+testDagger1.user1 +"\nuser2: "+ testDagger1.user2);
        System.out.println("testDagger2:\n user1: "+testDagger2.user1 +"\nuser2: "+ testDagger2.user2);
    }

    /**
     * 实验：为什么同一个TestDagger类对象中两个相同类型的变量（user1,user2）指向同一个对象，重新创建一个TestDagger对象(testDagger2)，里面的
     * 相同两个变量（user1,user2）虽然指向同一个对象，但是两个TestDagger对象(testDagger1,testDagger2)的相同类型User变量指向的却不是同意一个对象。
     * 到底Singleton的生命周期是和Module还是Component一样的？
     *
     * Component不同，Module相同
     * */
    private static void compareUser1WithUser2WithSameModule()
    {
        DemoModule module1 = new DemoModule();

        TestDagger testDagger1 = new TestDagger(module1);
        TestDagger testDagger2 = new TestDagger(module1);
        System.out.println("testDagger1:\nmodule1: "+module1+"\n user1: "+testDagger1.user1 +"\nuser2: "+ testDagger1.user2);
        System.out.println("testDagger2:\nmodule1: "+module1+"\n user1: "+testDagger2.user1 +"\nuser2: "+ testDagger2.user2);
    }


    /**
     * 相同Component对象,结果是两个TestDagger对象的User变量都指向同一个对象。这也不能很好的说明@Singleton标记的Provides方法和构造函数提供的实例在Component的生命
     * 周期内是不会变的，但是从官网：https://google.github.io/dagger/users-guide.html，对@Singleton有个描述
     *
     *
     * */
    private static void compareUser1WithUser2WithSameComponent()
    {
        DemoComponent component = DaggerDemoComponent.builder()
                .demoModule(new DemoModule())
                .build();
        TestDagger testDagger1 = new TestDagger(component);
        TestDagger testDagger2 = new TestDagger(component);
        System.out.println("testDagger1:\ncomponent: "+component+"\n user1: "+testDagger1.user1 +"\nuser2: "+ testDagger1.user2);
        System.out.println("testDagger2:\ncomponent: "+component+"\n user1: "+testDagger2.user1 +"\nuser2: "+ testDagger2.user2);
    }

    /**
     * 自定义Scope
     * 照着Singleton做就行，如MyScope
     * */
    private static void compareSuperUser1WithSuperUser2()
    {
        DemoMyScopeComponent component = DaggerDemoMyScopeComponent.builder()
                .build();
        TestDagger2 testDagger = new TestDagger2(component);
        System.out.println("superUser1: "+testDagger.superUser1 +"\nuser2: "+ testDagger.superUser2);
    }


    /**
     * @Qualifier的作用：
     *  假如一个Module中有多个返回相同类型的Provides方法，那么Inject时候又如何区分呢？打标签或者是做标记
     *  @Qualifier就相当于打标签的功能
     *
     *  首先需要自定义个@Qualifier注解
     * */
    private static void printVipUserWithQualifier()
    {
        TestDagger3 dagger3 = new TestDagger3();
        System.out.println("testDagger3 user1.vipNumber: "+dagger3.user1.getVipNumber() +"\nuser2.vipNumber: "+ dagger3.user2.getVipNumber());
    }






}
